package com.gitee.apanlh.util.dataformat.xml;

import com.gitee.apanlh.annotation.xstream.XStreamCDATA;
import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.cache.local.Cache;
import com.gitee.apanlh.util.cache.local.CacheUtils;
import com.gitee.apanlh.util.check.CheckImport;
import com.gitee.apanlh.util.check.CheckLibrary;
import com.gitee.apanlh.util.encode.CharsetCode;
import com.gitee.apanlh.util.reflection.ClassUtils;
import com.gitee.apanlh.util.reflection.ReflectionUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NameCoder;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;

import java.io.Writer;
import java.lang.reflect.Field;
import java.util.List;

/**
 * 	XStream自定义注解 用于解析{@code <![CDATA[]]>}
 * 
 * 	@author Pan
 */
public class XStreamFactory {
	
	static {
		CheckImport.library(CheckLibrary.XSTREAM);
	}
	
	/** 存储映射关系 */
	protected static final Cache<Class<?>, List<Field>> CLASS_ANNOTATION_MAPPING = CacheUtils.cache();
	/** 允许的类 */
	private static final String[] ALLOW_TYPES_BY_REGEXP = {".*"};
	/** CDATA */
	private static final String CDATA_PREFIX = "<![CDATA[";
	private static final String CDATA_SUFFIX = "]]>";
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private XStreamFactory() {
		//	不允许外部实例
		super();
	}
	
	/**	
	 * 	构建普通模式
	 * 	<br>默认UTF-8编码
	 * 
	 * 	@author Pan
	 * 	@param  <T>     数据类型
	 * 	@param 	obj			对象
	 * 	@return	XStream
	 */
	public static <T> XStream instance(T obj) {
		Assert.isNotNull(obj);
		
		return instance(obj.getClass());
	}
	
	/**	
	 * 	实例-普通模式
	 * 	<br>默认UTF-8编码
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz		类
	 * 	@return	XStream
	 */
	public static XStream instance(Class<?> clazz) {
		return instance(XStream.NO_REFERENCES, CharsetCode.UTF_8, new Class[] {clazz});
	}
	
	/**	
	 * 	实例-普通模式
	 * 	<br>默认UTF-8编码
	 * 
	 * 	@author Pan
	 * 	@param 	classes		类
	 * 	@return	XStream
	 */
	public static XStream instance(Class<?>[] classes) {
		return instance(XStream.NO_REFERENCES, CharsetCode.UTF_8, classes);
	}
	
	/**	
	 * 	实例-普通模式
	 * 	<br>自定义字符集编码
	 * 	
	 * 	@author Pan
	 * 	@param 	charset		字符集编码
	 * 	@param 	classes		类
	 * 	@return	XStream
	 */
	public static XStream instance(String charset, Class<?>[] classes) {
		return instance(XStream.NO_REFERENCES, charset, classes);
	}
	
	/**	
	 * 	实例-自定义mode
	 * 	
	 * 	@author Pan
	 * 	@param	mode		模式
	 * 	@param	charset		字符集编码
	 * 	@param 	classes		类
	 * 	@return	XStream
	 */
	public static XStream instance(int mode, String charset, Class<?>[] classes) {
		final NameCoder nameCoder = new NoNameCoder();
		
		XStream xStream = new XStream(new DomDriver(charset, nameCoder) {
			@Override
			public HierarchicalStreamWriter createWriter(Writer out) {
				return new PrettyPrintWriter(out, nameCoder) {
					boolean cdataFlag = false;
					Class<?> targetClass = null;

					@Override
					public void startNode(String name, Class clazz) {
						super.startNode(name, clazz);
						if (targetClass == null) {
							targetClass = clazz;
						}
						cdataFlag = ofCdata(targetClass, name);
					}

					@Override
					public void writeText(QuickWriter writer, String text) {
						if (cdataFlag) {
							writer.write(CDATA_PREFIX);
							writer.write(text);
							writer.write(CDATA_SUFFIX);
						} else {
							writer.write(text);
						}
					}
				};
			}
		});
		if (!ValidParam.isEmpty(classes)) {
			xStream.setClassLoader(classes[0].getClassLoader());
			xStream.processAnnotations(classes);
		}
		xStream.allowTypesByRegExp(ALLOW_TYPES_BY_REGEXP);
//		XStream.XPATH_RELATIVE_REFERENCES; 				-- by default 这是一个集成了W3C的XPATH标准的相对引用路径
//		XStream.XPATH_ABSOLUTE_REFERENCES; 				-- 集成了W3C的XPATH标准的绝对引用路径
//		XStream.SINGLE_NODE_XPATH_ABSOLUTE_REFERENCES; 	-- 单个节点的绝对引用路径(符合W3C的XPATH标准)
//		XStream.SINGLE_NODE_XPATH_RELATIVE_REFERENCES; 	-- 单个节点的相对引用路径(符合W3C的XPATH标准)
//		XStream.ID_REFERENCES; 							-- id属性引用，如果节点的attribute 中有id这一项的话，才可以使用了
//		XStream.NO_REFERENCES; 							-- 没有引用
		xStream.setMode(mode);
		return xStream;
	}
	

	/**		
	 * 	检测是否存在CDATA标识
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz      类
	 * 	@param 	fieldAlias 字段注解
	 * 	@return	boolean
	 */
	private static boolean ofCdata(Class<?> clazz, String fieldAlias) {
		// 检查类本身
		boolean cdataFlag = ofExistCdata(clazz, fieldAlias);
		if (cdataFlag) {
			return cdataFlag;
		}
		
		// 继续检查父类
		Class<?> superClazz = clazz.getSuperclass();
		while (!ClassUtils.eq(superClazz, Object.class)) {
			cdataFlag = ofExistCdata(superClazz, fieldAlias);
			if (!cdataFlag) {
				return false;
			}
			superClazz = superClazz.getClass().getSuperclass();
		}
		return false;
	}
	
	/**
	 * 	检查是否有 {@link XStreamCDATA} 注解
	 * 	
	 * 	@author Pan
	 * 	@param 	clazz      类
	 * 	@param 	fieldAlias 字段注解
	 * 	@return	boolean
	 */
	private static boolean ofExistCdata(Class<?> clazz, String fieldAlias) {
		List<Field> list = CLASS_ANNOTATION_MAPPING.get(clazz, () -> ReflectionUtils.getFieldAnnotation(clazz, XStreamCDATA.class, XStreamAlias.class));
		if (list == null) {
			return false;
		}
		
		Field field = CollUtils.findOne(list, t -> {
			XStreamAlias xStreamAlias = (XStreamAlias) ReflectionUtils.getFieldAnnotation(t, XStreamAlias.class);
			if (!StringUtils.eq(fieldAlias, xStreamAlias.value())) {
				return false;
			}
			return StringUtils.eq(fieldAlias, t.getName());
		});
		
		return field != null;
	}
}
